<?php

/**
 *		//curl类库//
 *		//这是一份试验品，由大街编写（zhongmingzi@gmail.com）
 *		//本程序默认环境：Zend Optimizer Yes / 3.3.3
 *		//		Apache/2.0.63 (Win32) PHP/5.2.12
 *		//		MySQL 支持 Yes / client lib version 5.0.89
 *		//		GD library Yes / bundled (2.0.34 compatible)
 *		//		eAccelerator/Memcache/xcache Yes
 *		//		cURL support enabled
 *		//时间：2011 02 26 22:57:49//
 */
if(!defined('IN')) {
	exit('Access Denied');
}

/**
 *@ 输入header数组例子
 *	$header []=  'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, text/html, * ' .  '/* ' ;  
 *	$header []=  'Accept-Language: zh-cn ' ;  
 *	$header []=  'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727) ' ;  
 *	$header []=  'Host: ' .$你的目标HOST;  
 *	$header []=  'Connection: Keep-Alive ' ;  
 *	$header []=  'Cookie: ' .$你的COOKIE串;  
 *@ exec的时候，返回的错误代码，取负数
 错误代码列表
	CURLE_UNSUPPORTED_PROTOCOL (1) – 您传送给 libcurl 的网址使用了此 libcurl 不支持的协议。 可能是您没有使用的编译时选项造成了这种情况（可能是协议字符串拼写有误，或没有指定协议 libcurl 代码）。
	CURLE_FAILED_INIT (2) - 非常早期的初始化代码失败。 可能是内部错误或问题。
	CURLE_URL_MALFORMAT (3) - 网址格式不正确。
	CURLE_COULDNT_RESOLVE_PROXY (5) - 无法解析代理服务器。 指定的代理服务器主机无法解析。
	CURLE_COULDNT_RESOLVE_HOST (6) – 无法解析主机。 指定的远程主机无法解析。
	CURLE_COULDNT_CONNECT (7) - 无法通过 connect() 连接至主机或代理服务器。
	CURLE_FTP_WEIRD_SERVER_REPLY (8) – 在连接到 FTP 服务器后，libcurl 需要收到特定的回复。 此错误代码表示收到了不正常或不正确的回复。 指定的远程服务器可能不是正确的 FTP 服务器。
	CURLE_REMOTE_ACCESS_DENIED (9) – 我们无法访问网址中指定的资源。 对于 FTP，如果尝试更改为远程目录，就会发生这种情况。
	CURLE_FTP_WEIRD_PASS_REPLY (11) - 在将 FTP 密码发送到服务器后，libcurl 需要收到正确的回复。 此错误代码表示返回的是意外的代码。
	CURLE_FTP_WEIRD_PASV_REPLY (13) – libcurl 无法从服务器端收到有用的结果，作为对 PASV 或 EPSV 命令的响应。 服务器有问题。
	CURLE_FTP_WEIRD_227_FORMAT (14) - FTP 服务器返回 227 行作为对 PASV 命令的响应。 如果 libcurl 无法解析此行，就会返回此代码。
	CURLE_FTP_CANT_GET_HOST (15) - 在查找用于新连接的主机时出现内部错误。
	CURLE_FTP_COULDNT_SET_TYPE (17) - 在尝试将传输模式设置为二进制或 ascii 时发生错误。
	CURLE_PARTIAL_FILE (18) - 文件传输尺寸小于或大于预期。 当服务器先报告了一个预期的传输尺寸，然后所传送的数据与先前指定尺寸不相符时，就会发生此错误。
	CURLE_FTP_COULDNT_RETR_FILE (19) - 'RETR' 命令收到了不正常的回复，或完成的传输尺寸为零字节。
	CURLE_QUOTE_ERROR (21) - 在向远程服务器发送自定义 "QUOTE" 命令时，其中一个命令返回的错误代码为 400 或更大的数字（对于 FTP），或以其他方式表明命令无法成功完成。
	CURLE_HTTP_RETURNED_ERROR (22) - 如果 CURLOPT_FAILONERROR 设置为 TRUE，且 HTTP 服务器返回 >= 400 的错误代码，就会返回此代码。 （此错误代码以前又称为 CURLE_HTTP_NOT_FOUND。）
	CURLE_WRITE_ERROR (23) - 在向本地文件写入所收到的数据时发生错误，或由写入回调 (write callback) 向 libcurl 返回了一个错误。
	CURLE_UPLOAD_FAILED (25) - 无法开始上传。 对于 FTP，服务器通常会拒绝执行 STOR 命令。 错误缓冲区通常会提供服务器对此问题的说明。 （此错误代码以前又称为 CURLE_FTP_COULDNT_STOR_FILE。）
	CURLE_READ_ERROR (26) - 读取本地文件时遇到问题，或由读取回调 (read callback) 返回了一个错误。
	CURLE_OUT_OF_MEMORY (27) - 内存分配请求失败。 此错误比较严重，若发生此错误，则表明出现了非常严重的问题。
	CURLE_OPERATION_TIMEDOUT (28) - 操作超时。 已达到根据相应情况指定的超时时间。 请注意： 自 Urchin 6.6.0.2 开始，超时时间可以自行更改。 要指定远程日志下载超时，请打开 urchin.conf 文件，取消以下行的注释标记：
	#DownloadTimeout: 30
	CURLE_FTP_PORT_FAILED (30) - FTP PORT 命令返回错误。 在没有为 libcurl 指定适当的地址使用时，最有可能发生此问题。 请参阅 CURLOPT_FTPPORT。
	CURLE_FTP_COULDNT_USE_REST (31) - FTP REST 命令返回错误。 如果服务器正常，则应当不会发生这种情况。
	CURLE_RANGE_ERROR (33) - 服务器不支持或不接受范围请求。
	CURLE_HTTP_POST_ERROR (34) - 此问题比较少见，主要由内部混乱引发。
	CURLE_SSL_CONNECT_ERROR (35) - 同时使用 SSL/TLS 时可能会发生此错误。 您可以访问错误缓冲区查看相应信息，其中会对此问题进行更详细的介绍。 可能是证书（文件格式、路径、许可）、密码及其他因素导致了此问题。
	CURLE_FTP_BAD_DOWNLOAD_RESUME (36) - 尝试恢复超过文件大小限制的 FTP 连接。
	CURLE_FILE_COULDNT_READ_FILE (37) - 无法打开 FILE:// 路径下的文件。 原因很可能是文件路径无法识别现有文件。 建议您检查文件的访问权限。
	CURLE_LDAP_CANNOT_BIND (38) - LDAP 无法绑定。LDAP 绑定操作失败。
	CURLE_LDAP_SEARCH_FAILED (39) - LDAP 搜索无法进行。
	CURLE_FUNCTION_NOT_FOUND (41) - 找不到函数。 找不到必要的 zlib 函数。
	CURLE_ABORTED_BY_CALLBACK (42) - 由回调中止。 回调向 libcurl 返回了 "abort"。
	CURLE_BAD_FUNCTION_ARGUMENT (43) - 内部错误。 使用了不正确的参数调用函数。
	CURLE_INTERFACE_FAILED (45) - 界面错误。 指定的外部界面无法使用。 请通过 CURLOPT_INTERFACE 设置要使用哪个界面来处理外部连接的来源 IP 地址。 （此错误代码以前又称为 CURLE_HTTP_PORT_FAILED。）
	CURLE_TOO_MANY_REDIRECTS (47) - 重定向过多。 进行重定向时，libcurl 达到了网页点击上限。 请使用 CURLOPT_MAXREDIRS 设置上限。
	CURLE_UNKNOWN_TELNET_OPTION (48) - 无法识别以 CURLOPT_TELNETOPTIONS 设置的选项。 请参阅相关文档。
	CURLE_TELNET_OPTION_SYNTAX (49) – telnet 选项字符串的格式不正确。
	CURLE_PEER_FAILED_VERIFICATION (51) - 远程服务器的 SSL 证书或 SSH md5 指纹不正确。
	CURLE_GOT_NOTHING (52) - 服务器未返回任何数据，在相应情况下，未返回任何数据就属于出现错误。
	CURLE_SSL_ENGINE_NOTFOUND (53) - 找不到指定的加密引擎。
	CURLE_SSL_ENGINE_SETFAILED (54) - 无法将选定的 SSL 加密引擎设为默认选项。
	CURLE_SEND_ERROR (55) - 无法发送网络数据。
	CURLE_RECV_ERROR (56) - 接收网络数据失败。
	CURLE_SSL_CERTPROBLEM (58) - 本地客户端证书有问题
	CURLE_SSL_CIPHER (59) - 无法使用指定的密钥
	CURLE_SSL_CACERT (60) - 无法使用已知的 CA 证书验证对等证书
	CURLE_BAD_CONTENT_ENCODING (61) - 无法识别传输编码
	CURLE_LDAP_INVALID_URL (62) - LDAP 网址无效
	CURLE_FILESIZE_EXCEEDED (63) – 超过了文件大小上限
	CURLE_USE_SSL_FAILED (64) - 请求的 FTP SSL 级别失败
	CURLE_SEND_FAIL_REWIND (65) – 进行发送操作时，curl 必须回转数据以便重新传输，但回转操作未能成功
	CURLE_SSL_ENGINE_INITFAILED (66) - SSL 引擎初始化失败
	CURLE_LOGIN_DENIED (67) - 远程服务器拒绝 curl 登录（7.13.1 新增功能）
	CURLE_TFTP_NOTFOUND (68) - 在 TFTP 服务器上找不到文件
	CURLE_TFTP_PERM (69) - 在 TFTP 服务器上遇到权限问题
	CURLE_REMOTE_DISK_FULL (70) - 服务器磁盘空间不足
	CURLE_TFTP_ILLEGAL (71) - TFTP 操作非法
	CURLE_TFTP_UNKNOWNID (72) - TFTP 传输 ID 未知
	CURLE_REMOTE_FILE_EXISTS (73) - 文件已存在，无法覆盖
	CURLE_TFTP_NOSUCHUSER (74) - 运行正常的 TFTP 服务器不会返回此错误
	CURLE_CONV_FAILED (75) – 字符转换失败
	CURLE_CONV_REQD (76) - 调用方必须注册转换回调
	CURLE_SSL_CACERT_BADFILE (77) - 读取 SSL CA 证书时遇到问题（可能是路径错误或访问权限问题）
	CURLE_REMOTE_FILE_NOT_FOUND (78) - 网址中引用的资源不存在
	CURLE_SSH (79) - SSH 会话中发生无法识别的错误
	CURLE_SSL_SHUTDOWN_FAILED (80) – 无法终止 SSL 连接
 */

class curl {

	private $url;
	public $pre = '';//用户标示
	public $mod = '';//应用标示，比如保存163登录
	private $ch;

	public $error = '';
	public $errno = 0;
	public $is_info = 0;//是否开启调试info
	private $info;
	public $is_crlf = 0;
	public $port = 80;
	public $pre_cookie_path = '';//理论上存在的cookie file路径
	public $cookie_file = '';//理论存在的cookie file地址

	private $cookie_session = '';//如果有session cookie则最大优先级
	private $cookie = '';//如果这里设置了，则传递set-cookie
	public $read_jar = 0;//如果启用，则会判断是否存在该cookie file文件并传递
	public $save_jar = 0;//如果启用，则保存本次cookie到文件，如果有设置mod，pre保存到相应目录
	public $referer = '';
	private $httpheader = 0;//这里设置头信息数组，如果存在且是数组的话

	public $header = 0;//是否启用输出头信息
	public $nobody = 0;//是否不输出body内容

	function __construct() {
		$this->errno = 0;
		$this->ch = curl_init();
		if(!$this->ch) {
			$this->errno = -10;
			return -10;
		}
		
		//如果有跳转，则跳转
		curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
		//如果跳转，设置最大跳转次数
		curl_setopt($this->ch, CURLOPT_MAXREDIRS, 10);
		//自动设置跳转的referer为上一个跳转地址
		curl_setopt($this->ch, CURLOPT_AUTOREFERER, 1);
		//curl连接的时候，允许的最大时间
		curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, 20);
		//curl执行的时候，允许的最大时间
		curl_setopt($this->ch, CURLOPT_TIMEOUT, 30);

		//在HTTP请求中包含一个“user-agent”头的字符串
		curl_setopt($this->ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.497.0 Safari/534.6");
		//将curl_exec()获取的信息以文件流的形式返回，而不是直接输出。
		curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($this->ch, CURLOPT_BINARYTRANSFER, 1);
	}

	//mod和pre表示某模块中某用户的登录信息，一般要同时存在
	function setting($mod = '', $pre = '') {
		if($this->errno != 0) return $this->errno;
		$this->mod = $mod;
		$this->pre = $pre;
		if($mod != '' && $pre != '') $this->pre_cookie_path = curl::cookie_dir($this->mod, $this->pre);

		//在HTTP请求中包含一个”referer”头的字符串
		if($this->referer) curl_setopt($this->ch, CURLOPT_REFERER, $this->referer);
		//设置CURL连接的端口
		curl_setopt($this->ch, CURLOPT_PORT, $this->port);

		//如果存在session，则值传递session cookie而忽略其他的
		if($this->cookie_session) curl_setopt($this->ch, CURLOPT_COOKIESESSION, $this->cookie_session);
		//如果已经保存了cookie到file，这里读取这个cookie file
		if($this->pre_cookie_path != '') $this->cookie_file = $this->pre_cookie_path.$this->pre."_cookies.txt";
		if($this->read_jar && file_exists($this->cookie_file)) curl_setopt($this->ch, CURLOPT_COOKIEFILE, $this->cookiefile);
		//设定HTTP请求中“Set-Cookie:”部分的内容，优先级大于上面的
		if($this->cookie) curl_setopt($this->ch, CURLOPT_COOKIE, $this->cookie);
		//如果有头信息数组，则设置，这里有最大优先级
		if($this->header && is_array($this->header)) curl_setopt($this->ch ,CURLOPT_HTTPHEADER, $this->httpheader);

		//如果设置保存cookie，而且设置了模块用户，则关闭时保存到文件
		if($this->save_jar) {
			$this->pre_cookie_path = $this->pre_cookie_path == '' ? ROOT."./data/temp/" : $this->pre_cookie_path;
			if(!file_exists($this->pre_cookie_path)) dmkdir($this->pre_cookie_path);
			$this->cookiefile = $this->pre_cookie_path.$this->pre."_cookies.txt";
			curl_setopt($this->ch, CURLOPT_COOKIEJAR, $this->cookiefile);
		}

		//输出获取的头信息
		curl_setopt($this->ch, CURLOPT_HEADER, $this->header);
		//输出不包含body信息
		curl_setopt($this->ch, CURLOPT_NOBODY, $this->nobody);
		//启用时将Unix的换行符转换成回车换行符
		curl_setopt($this->ch, CURLOPT_CRLF, $this->is_crlf);
	}

	function close() {
		curl_close($this->ch);
	}

	function post($params = array()) {
		if($this->errno != 0) return $this->errno;
		curl_setopt($this->ch, CURLOPT_POST, 1);
		// 把post的变量加上
		curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($params));
	}
	
	function exec($url) {
		if($this->errno != 0) return $this->errno;
		$this->url = $url;
		curl_setopt($this->ch, CURLOPT_URL, $this->url);
		$ret = curl_exec($this->ch);
		if(empty($ret)) {
			$errno = curl_errno($this->ch);//错误编号从1~6,0为正常
			$this->errno = 0 - $errno;//取相反数
			$this->error = curl_error($this->ch);
			if($this->is_info) $this->info = curl_getinfo($this->ch);
			else curl_close($this->ch);
			return $this->errno;
		} else {
			if($this->is_info) $this->info = curl_getinfo($this->ch);
			else curl_close($this->ch); 
			return $ret;
		}
	}
	
	//这个函数把成功的cookie移到另外一个地方
	static function cookie_dir($mod, $pre, $isrename = 0, $cookiepath = '') {
		$keylen = strlen($pre);
		$keyoff = ceil($keylen/2);
		$path_a = ord(substr($pre, 0, 1));
		$path_b = ord(substr($pre, $keyoff, 1));
		$path_c = ord(substr($pre, -1, 1));
		$path = $path_a.'/'.$path_b.'/'.$path_c."/";
		$new = "./data/cookie/".$mod."/".$path;

		if($isrename == 0) {
			return $new;
		} else {
			$newpath = $new.$pre."_cookies.txt";
			rename($cookiepath, $newpath);
		}
	}
}

?>
